<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="Python 单元测试（unittest）">
<meta name="twitter:description" content="<p>单元测试，Python标准库unittest模块的使用。</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/python-unittest.html"/>
<meta property="og:title" content="来玩魔王的咚 | Python 单元测试（unittest）" />
<meta property="og:description" content="<p>单元测试，Python标准库unittest模块的使用。</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>单元测试，Python标准库unittest模块的使用。</p>" />

    <meta name="tags" content="测试" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li class="active"><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2021-04-24 14:45:00+08:00">
                <script>document.write(moment('2021-04-24 14:45:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2021-04-24 14:45:00+08:00">
                <script>document.write(moment('2021-04-24 18:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            Python 单元测试（unittest）
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#zhun-bei-gong-zuo">准备工作</a><ul>
<li><a href="#ce-shi-mu-lu">测试目录</a></li>
<li><a href="#zai-vscode-zhong-pei-zhi">在vscode中配置</a></li>
</ul>
</li>
<li><a href="#ce-shi-han-shu">测试函数</a><ul>
<li><a href="#shou-dong-ce-shi">手动测试</a></li>
<li><a href="#mo-kuai-dao-ru-de-wen-ti">模块导入的问题</a></li>
<li><a href="#dan-yuan-ce-shi-unittest">单元测试-unittest</a></li>
<li><a href="#ce-shi-bu-tong-guo">测试不通过</a></li>
</ul>
</li>
<li><a href="#jin-jie">进阶</a><ul>
<li><a href="#shi-yong-biao-ge-jin-xing-ce-shi">使用表格进行测试</a></li>
<li><a href="#duan-yan-fang-fa">断言方法</a></li>
<li><a href="#setup-yu-teardown">setUp与tearDown</a></li>
<li><a href="#cha-jian-tui-jian">插件推荐</a></li>
</ul>
</li>
<li><a href="#ce-shi-lei">测试类</a><ul>
<li><a href="#zhun-bei-yao-ce-shi-de-lei">准备要测试的类</a></li>
<li><a href="#shou-dong-ce-shi_1">手动测试</a></li>
<li><a href="#bian-xie-lei-de-ce-shi-yong-li">编写类的测试用例</a></li>
<li><a href="#setup-fang-fa">setUp() 方法</a></li>
</ul>
</li>
<li><a href="#zong-jie">总结</a></li>
</ul>
</div>
<p>Python 官方文档：<br>
<a href="https://docs.python.org/zh-cn/3/library/unittest.html">https://docs.python.org/zh-cn/3/library/unittest.html</a>  </p>
<p>VScode 的相关文档：<br>
<a href="https://code.visualstudio.com/docs/python/testing">https://code.visualstudio.com/docs/python/testing</a>  </p>
<h3 id="zhun-bei-gong-zuo"><a class="toclink" href="#zhun-bei-gong-zuo">准备工作</a></h3>
<p>测试前环境的配置，包括VScode的配置。  </p>
<h4 id="ce-shi-mu-lu"><a class="toclink" href="#ce-shi-mu-lu">测试目录</a></h4>
<p>一般都是单独创建一个测试目录，目录名就叫tests，测试的python代码全都放在tests文件夹中。<br>
位置建议是项目下的二级子目录<code>Foo/foo/tests</code>，这种情况，一般项目的核心代码都在<code>Foo/foo</code>文件夹里。<br>
也可以把测试目录向上移一层，作为一级子目录，直接放在项目之下<code>Foo/tests</code>。参考django、scrapy、flask这些项目的做法。  </p>
<h4 id="zai-vscode-zhong-pei-zhi"><a class="toclink" href="#zai-vscode-zhong-pei-zhi">在vscode中配置</a></h4>
<p>首先在项目根目录下创建tests文件夹，然后可以用VScode的交互式方式完成基本的配置：<br>
快捷键：<code>Ctrl + Shift + P</code>，执行<code>&gt;Python: 配置单元测试</code>。<br>
选择使用哪种单元测试：unittest,pytest,nose。这里选unittest。<br>
选择测试文件的搜索目录：选择之前创建的tests目录。<br>
测试文件的文件名匹配模式，有下面几个建议的选项：</p>
<ol>
<li><code>*test.py</code></li>
<li><code>*_test.py</code></li>
<li><code>test*.py</code></li>
<li><code>test_*.py</code></li>
<li><code>*test*.py</code></li>
</ol>
<p>这里我选择第二个。<br>
至此完成了基本的配置，并且生成了配置文件（setting.json）：</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nt">&quot;python.testing.unittestArgs&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;-v&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;-s&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;./tests&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;-p&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="s2">&quot;*_test.py&quot;</span><span class="w"></span>
<span class="w">    </span><span class="p">],</span><span class="w"></span>
<span class="w">    </span><span class="nt">&quot;python.testing.pytestEnabled&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
<span class="w">    </span><span class="nt">&quot;python.testing.nosetestsEnabled&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
<span class="w">    </span><span class="nt">&quot;python.testing.unittestEnabled&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>所以，也可以不运行上面的向导，直接添加配置，或者根据需要直接调整配置文件。  </p>
<h3 id="ce-shi-han-shu"><a class="toclink" href="#ce-shi-han-shu">测试函数</a></h3>
<p>标题的意思是对函数（def）进行测试，相对于测试类（class）。<br>
学习测试，得有要测试的代码。下面是一个简单的函数，接收城市名和国家名，返回一个格式为“City, Country“这样的字符串：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/unit_test/utils/city_functions.py</span>
<span class="k">def</span> <span class="nf">get_city_info</span><span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">):</span>
    <span class="n">city_info</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">, </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">city_info</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
</code></pre></div>

<p>接下来就对上面的这个函数进行测试。  </p>
<h4 id="shou-dong-ce-shi"><a class="toclink" href="#shou-dong-ce-shi">手动测试</a></h4>
<p>现在来写一个使用这个函数的程序：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/tests/cities.py</span>
<span class="k">try</span><span class="p">:</span>
    <span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>
<span class="k">except</span> <span class="ne">ModuleNotFoundError</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))))</span>
    <span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>

<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Enter &#39;q&#39; at any time to quit.&quot;</span><span class="p">)</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
    <span class="n">city</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">&quot;city name: &quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">city</span> <span class="o">==</span> <span class="s1">&#39;q&#39;</span><span class="p">:</span>
        <span class="k">break</span>
    <span class="n">country</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">&quot;country name: &quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">country</span> <span class="o">==</span> <span class="s1">&#39;q&#39;</span><span class="p">:</span>
        <span class="k">break</span>
    <span class="n">fullname</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">)</span>
    <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\t</span><span class="s2">city info:&quot;</span><span class="p">,</span> <span class="n">fullname</span><span class="p">)</span>
</code></pre></div>

<p>这个手动测试的文件，并没有使用unittest，所以文件没有按照unittest的文件名匹配模式来命名。  </p>
<p>运行结果：</p>
<div class="highlight"><pre><span></span><code>Enter &#39;q&#39; at any time to quit.
city name: shanghai
country name: china
        city info: Shanghai, China
city name: q
</code></pre></div>

<p>上面这样是手动测试，还是得有一种自动测试函数输出的高效方式。如果能够对get_fullname()进行自动测试，就能始终确信，给这个函数提供测试过的姓名后，它能返回正确的结果。尤其是在对函数进行修改的前后。  </p>
<h4 id="mo-kuai-dao-ru-de-wen-ti"><a class="toclink" href="#mo-kuai-dao-ru-de-wen-ti">模块导入的问题</a></h4>
<p>测试文件仅作为模块运行没有问题，但是测试文件不能直接执行，会有模块导入的问题，无法导致模块。需要将项目的根目录添加到模块搜索路径（sys.path）中。  </p>
<p>上面的例子使用的方法单独贴出来：</p>
<div class="highlight"><pre><span></span><code><span class="k">try</span><span class="p">:</span>
    <span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>
<span class="k">except</span> <span class="ne">ModuleNotFoundError</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))))</span>
    <span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>
</code></pre></div>

<p>这个方式，程序可以在任意的工作目录执行。但是程序文件一定要在项目下的一级子目录里。<br>
就是程序文件必须要在项目下，并且所在的层级不能变。<br>
从哪个位置调用执行这个文件无所谓。  </p>
<p>下面的方法似乎更好：</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>

<span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>
</code></pre></div>

<p><strong>注意：</strong>代码的位置仍然是程序的最上面。<br>
这个方式，程序文件可以在任何位置，甚至可以不再项目路径里。<br>
但是必须保证工作目录是项目的根目录，也就是必须在项目的根目录下执行。<br>
一般都是在项目根目录下跑的，现在看来，这个方式更好。  </p>
<h4 id="dan-yuan-ce-shi-unittest"><a class="toclink" href="#dan-yuan-ce-shi-unittest">单元测试-unittest</a></h4>
<p>Python标准库中的模块unittest提供了代码测试工具。  </p>
<h5 id="chuang-jian-ce-shi-yong-li"><a class="toclink" href="#chuang-jian-ce-shi-yong-li">创建测试用例</a></h5>
<p>为函数编写测试用例，可先导入模块unittest以及要测试的函数，再创建一个继承unittest.TestCase的类，并编写一系列方法对函数行为的不同方面进行测试。<br>
下面是一个只包含一个方法的测试用例：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/tests/city_functions_test.py</span>
<span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>

<span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>

<span class="k">class</span> <span class="nc">CitiesTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;测试city_functions.py&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">test_city_country</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="s1">&#39;shanghai&#39;</span><span class="p">,</span> <span class="s1">&#39;china&#39;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">city_info</span><span class="p">,</span> <span class="s1">&#39;Shanghai, China&#39;</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div>

<p>命名的规则和建议：</p>
<ul>
<li>类名，可以任意起名，但是最好看起来和测试有关并包含Test字样。</li>
<li>方法名，名字必须以“test_”开头，所有以“test_”开头的方法，都会自动运行</li>
</ul>
<p>在测试的方法的最后，使用了unittest类最有用的功能之一：一个断言方法。来检查得到的结果和我们预期的结果是否一致。  </p>
<p>最后一行 unittest.main() 让Python运行这个文件中的测试。执行程序后得到如下的输出：</p>
<div class="highlight"><pre><span></span><code>.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
</code></pre></div>

<p>运行测试用例时，每完成一个单元测试，Python都打印一个字符（第一行有1个点）：</p>
<ul>
<li>测试通过时打印一个句点；</li>
<li>测试引发错误时打印一个E；</li>
<li>测试导致断言失败时打印一个F。</li>
</ul>
<p>这就是你运行测试用例时，在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试，需要运行很长时间，就可通过观察这些结果来获悉有多少个测试通过了。  </p>
<h4 id="ce-shi-bu-tong-guo"><a class="toclink" href="#ce-shi-bu-tong-guo">测试不通过</a></h4>
<p>现在看下测试不通过的效果。这里不修改测试用例，而是对get_city_info()函数做一个update，现在还可以显示城市的人口数量：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/unit_test/utils/city_functions.py</span>
<span class="k">def</span> <span class="nf">get_city_info</span><span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">,</span> <span class="n">population</span><span class="p">):</span>
    <span class="n">city_info</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">, </span><span class="si">%s</span><span class="s2"> - 人口: </span><span class="si">%d</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">,</span> <span class="n">population</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">city_info</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
</code></pre></div>

<p>这次再执行测试用例，输出如下：</p>
<div class="highlight"><pre><span></span><code>E
======================================================================
ERROR: test_city_country (__main__.CitiesTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File &quot;i:\gitee.com\localproject\UnitTest\tests\city_functions_test.py&quot;, line 20, in test_city_country
    city_info = get_city_info(&#39;shanghai&#39;, &#39;china&#39;)
TypeError: get_city_info() missing 1 required positional argument: &#39;population&#39;

----------------------------------------------------------------------
Ran 1 tests in 0.000s

FAILED (errors=1)
</code></pre></div>

<p>这里看的是E而不是之前的点，表示有错误。  </p>
<h5 id="ce-shi-wei-tong-guo-de-chu-li"><a class="toclink" href="#ce-shi-wei-tong-guo-de-chu-li">测试未通过的处理</a></h5>
<p>这里不要去修改之前的测试用例。假设update之前的函数已经在项目内使用起来了，现在测试不通过，表示之前调用这个函数的代码都有问题。<br>
如果不想改项目里其它的代码，这里先尝试修改get_city_info()函数，让它既能够通过测试，也可以加上新的功能：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/unit_test/utils/city_functions.py</span>
<span class="k">def</span> <span class="nf">get_city_info</span><span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">,</span> <span class="n">population</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">population</span><span class="p">:</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">, </span><span class="si">%s</span><span class="s2"> - 人口: </span><span class="si">%d</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">,</span> <span class="n">population</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="s2">, </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">city</span><span class="p">,</span> <span class="n">country</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">city_info</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
</code></pre></div>

<p>现在的各个版本的update才是兼容旧版本的代码。现在再去跑测试用例，就可以通过了。  </p>
<h5 id="tian-jia-xin-ce-shi"><a class="toclink" href="#tian-jia-xin-ce-shi">添加新测试</a></h5>
<p>之前的测试用例只能验证旧的功能，现在添加了新功能，是否没问题，还得通过测试来进行验证：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/tests/city_functions_test.py</span>
<span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>

<span class="kn">from</span> <span class="nn">unit_test.utils.city_functions</span> <span class="kn">import</span> <span class="n">get_city_info</span>

<span class="k">class</span> <span class="nc">CitiesTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;测试city_functions.py&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">test_city_country</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="s1">&#39;shanghai&#39;</span><span class="p">,</span> <span class="s1">&#39;china&#39;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">city_info</span><span class="p">,</span> <span class="s1">&#39;Shanghai, China&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_New_York</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="s1">&#39;new york&#39;</span><span class="p">,</span> <span class="s1">&#39;America&#39;</span><span class="p">,</span> <span class="mi">8537673</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">city_info</span><span class="p">,</span> <span class="s1">&#39;New York, America - 人口: 8537673&#39;</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div>

<p>原来的测试用例留着，用于测试旧版可以兼容。新的功能通过增加一个新的测试用例来测试。现在2个测试都能通过。<br>
以后如果还需要对get_city_info()函数进行修改，只要再运行测试就可以知道新的代码是否会对原有的项目有影响。  </p>
<h3 id="jin-jie"><a class="toclink" href="#jin-jie">进阶</a></h3>
<h4 id="shi-yong-biao-ge-jin-xing-ce-shi"><a class="toclink" href="#shi-yong-biao-ge-jin-xing-ce-shi">使用表格进行测试</a></h4>
<p>为每一个测试用例编写一个方法也很麻烦。可以在一个测试中，调用多个子测试，输入和输出都先写到表里，然后遍历这个表：</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">CitiesTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;测试city_functions.py&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">test_city_country</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="s1">&#39;shanghai&#39;</span><span class="p">,</span> <span class="s1">&#39;china&#39;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">city_info</span><span class="p">,</span> <span class="s1">&#39;Shanghai, China&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_New_York</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">city_info</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="s1">&#39;new york&#39;</span><span class="p">,</span> <span class="s1">&#39;America&#39;</span><span class="p">,</span> <span class="mi">8537673</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">city_info</span><span class="p">,</span> <span class="s1">&#39;New York, America - 人口: 8537673&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_table</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">table</span> <span class="o">=</span> <span class="p">[</span>
            <span class="p">((</span><span class="s1">&#39;shanghai&#39;</span><span class="p">,</span> <span class="s1">&#39;china&#39;</span><span class="p">),</span> <span class="s1">&#39;Shanghai, China&#39;</span><span class="p">),</span>
            <span class="p">((</span><span class="s1">&#39;new york&#39;</span><span class="p">,</span> <span class="s1">&#39;America&#39;</span><span class="p">,</span> <span class="mi">8537673</span><span class="p">),</span> <span class="s1">&#39;New York, America - 人口: 8537673&#39;</span><span class="p">),</span>
            <span class="p">((</span><span class="s1">&#39;guangzhou&#39;</span><span class="p">,</span> <span class="s1">&#39;china&#39;</span><span class="p">),</span> <span class="s1">&#39;Guangzhou, China&#39;</span><span class="p">),</span>
            <span class="p">((</span><span class="s1">&#39;sao paulo&#39;</span><span class="p">,</span> <span class="s1">&#39;Brazil&#39;</span><span class="p">),</span> <span class="s1">&#39;Sao Paulo, Crazil&#39;</span><span class="p">),</span>
        <span class="p">]</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">table</span><span class="p">:</span>
            <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">subTest</span><span class="p">():</span>
                <span class="n">args</span> <span class="o">=</span> <span class="n">i</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
                <span class="n">res</span> <span class="o">=</span> <span class="n">get_city_info</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="n">i</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div>

<p>同样的测试，可以提供多组数据来验证。<br>
<strong>建议：</strong>为每种特殊的情况编写单独的测试，为普遍的情况，提供多组输入输出，写一个表格测试。<br>
比如测试一个处理列表的函数，不要忘记空列表、仅一个元素的列表这类特殊情况，编写专门的测试函数来验证。  </p>
<h4 id="duan-yan-fang-fa"><a class="toclink" href="#duan-yan-fang-fa">断言方法</a></h4>
<p>模块在unittest.TestCase类中提供了很多断言方法，之前已经用一个了。下面是基础的常用的断言方法：</p>
<table>
<thead>
<tr>
<th align="left">Method</th>
<th align="left">Checks that</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">assertEqual(a, b)</td>
<td align="left">a == b</td>
</tr>
<tr>
<td align="left">assertNotEqual(a, b)</td>
<td align="left">a != b</td>
</tr>
<tr>
<td align="left">assertTrue(x)</td>
<td align="left">bool(x) is True</td>
</tr>
<tr>
<td align="left">assertFalse(x)</td>
<td align="left">bool(x) is False</td>
</tr>
<tr>
<td align="left">assertIs(a, b)</td>
<td align="left">a is b</td>
</tr>
<tr>
<td align="left">assertIsNot(a, b)</td>
<td align="left">a is not b</td>
</tr>
<tr>
<td align="left">assertIsNone(x)</td>
<td align="left">x is None</td>
</tr>
<tr>
<td align="left">assertIsNotNone(x)</td>
<td align="left">x is not None</td>
</tr>
<tr>
<td align="left">assertIn(a, b)</td>
<td align="left">a in b</td>
</tr>
<tr>
<td align="left">assertNotIn(a, b)</td>
<td align="left">a not in b</td>
</tr>
<tr>
<td align="left">assertIsInstance(a, b)</td>
<td align="left">isinstance(a, b)</td>
</tr>
<tr>
<td align="left">assertIsInstance(a, b)</td>
<td align="left">isinstance(a, b)</td>
</tr>
<tr>
<td align="left">assertNotIsInstance(a, b)</td>
<td align="left">not isinstance(a, b)</td>
</tr>
</tbody>
</table>
<p>你只能在继承unittest.TestCase的类中使用这些方法。<br>
还有更多的断言方法，去查阅官方文档：<br>
<a href="https://docs.python.org/zh-cn/3/library/unittest.html#test-cases">https://docs.python.org/zh-cn/3/library/unittest.html#test-cases</a>  </p>
<h4 id="setup-yu-teardown"><a class="toclink" href="#setup-yu-teardown">setUp与tearDown</a></h4>
<p>可以在单元测试中编写两个特殊的setUp()和tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。<br>
比如，你的测试需要启动一个数据库。这时，就可以在setUp()方法中连接数据库，在tearDown()方法中关闭数据库。这样，不必在每个测试方法中重复相同的代码，也不会多次连接数据库：</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">TestDict</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;setUp...&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">tearDown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;tearDown...&#39;</span><span class="p">)</span>
</code></pre></div>

<p>这里没有例子，在下面类的测试中，会用到setUp()方法，在测试前，生成测试用的实例对象。  </p>
<h4 id="cha-jian-tui-jian"><a class="toclink" href="#cha-jian-tui-jian">插件推荐</a></h4>
<p>使用VScode的话，建议使用这个插件：<br>
Python Test Explorer for Visual Studio Code  </p>
<h3 id="ce-shi-lei"><a class="toclink" href="#ce-shi-lei">测试类</a></h3>
<p>前面的内容只是对函数进行测试。很多时候都会用到类，因为还需要能够证明类也可以正常的运行。类的测试与函数的测试相似，其中大部分工作都是测试类中方法的行为，但存在一些不同之处。  </p>
<h4 id="zhun-bei-yao-ce-shi-de-lei"><a class="toclink" href="#zhun-bei-yao-ce-shi-de-lei">准备要测试的类</a></h4>
<p>先编写一个类来进行测试，这个类里存储了一个课程名，以及学习该课程的学员：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/unit_test/course.py</span>
<span class="k">class</span> <span class="nc">CourseManage</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>

    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">course</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">course</span> <span class="o">=</span> <span class="n">course</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">students</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">def</span> <span class="nf">show_course</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;课程:&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">course</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">add_student</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">students</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">show_students</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;所有学员:&quot;</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">student</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">students</span><span class="p">:</span>
            <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="n">student</span><span class="p">)</span>
</code></pre></div>

<h4 id="shou-dong-ce-shi_1"><a class="toclink" href="#shou-dong-ce-shi_1">手动测试</a></h4>
<p>为证明CourseManage类工作正常，下面是一段使用它的程序：</p>
<div class="highlight"><pre><span></span><code><span class="n">course</span> <span class="o">=</span> <span class="n">CourseManage</span><span class="p">(</span><span class="s2">&quot;Python&quot;</span><span class="p">)</span>
<span class="n">course</span><span class="o">.</span><span class="n">show_course</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;准备录入学员...&quot;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Enter &#39;q&#39; at any time to quit.</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
    <span class="n">resp</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">&quot;Student&#39;s Name: &quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">resp</span> <span class="o">==</span> <span class="s1">&#39;q&#39;</span><span class="p">:</span>
        <span class="k">break</span>
    <span class="k">if</span> <span class="n">resp</span><span class="p">:</span>
        <span class="n">course</span><span class="o">.</span><span class="n">add_student</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">title</span><span class="p">())</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">录入完毕...&quot;</span><span class="p">)</span>
<span class="n">course</span><span class="o">.</span><span class="n">show_students</span><span class="p">()</span>
</code></pre></div>

<p>这段程序定义了一门课程，并使用课程名创建了一个CourseManage对象。接下来主要就是调用对象的add_student()方法来录入学员名字。输入完毕后，按q能退出。最后会打印所有的学员。<br>
所有的输入和输出如下：</p>
<div class="highlight"><pre><span></span><code>课程: Python
准备录入学员...
Enter &#39;q&#39; at any time to quit.

Student&#39;s Name: Python
Student&#39;s Name: oliver queen
Student&#39;s Name: barry allen
Student&#39;s Name: kara
Student&#39;s Name: sara lance
Student&#39;s Name: q

录入完毕...
所有学员:
- Python
- Oliver Queen
- Barry Allen
- Kara
- Sara Lance
</code></pre></div>

<h4 id="bian-xie-lei-de-ce-shi-yong-li"><a class="toclink" href="#bian-xie-lei-de-ce-shi-yong-li">编写类的测试用例</a></h4>
<p>下面来编写一个测试，对CourseManage类的行为的一个方面进行验证。如果用户输入了某个学员的名字，这个名字可以被存储在self.students的列表里。所以，需要做的是在学员被录入后，使用assertIn()这个断言方法：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/tests/course_test.py</span>
<span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>

<span class="kn">from</span> <span class="nn">unit_test.course</span> <span class="kn">import</span> <span class="n">CourseManage</span>

<span class="k">class</span> <span class="nc">TestCourseManage</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">test_add_student</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">course</span> <span class="o">=</span> <span class="n">CourseManage</span><span class="p">(</span><span class="s2">&quot;Python&quot;</span><span class="p">)</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;snart&#39;</span>
        <span class="n">course</span><span class="o">.</span><span class="n">add_student</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">())</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertIn</span><span class="p">(</span><span class="s1">&#39;Snart&#39;</span><span class="p">,</span> <span class="n">course</span><span class="o">.</span><span class="n">students</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div>

<p>上面的方法只验证了录入一个学员的情况，而大多数情况下都是有很多学员的。所以，还要添加一个方法，验证录入多个学员是否正常：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/tests/course_test.py</span>
<span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>

<span class="kn">from</span> <span class="nn">unit_test.course</span> <span class="kn">import</span> <span class="n">CourseManage</span>

<span class="k">class</span> <span class="nc">TestCourseManage</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">test_add_student</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">course</span> <span class="o">=</span> <span class="n">CourseManage</span><span class="p">(</span><span class="s2">&quot;Python&quot;</span><span class="p">)</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;snart&#39;</span>
        <span class="n">course</span><span class="o">.</span><span class="n">add_student</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">())</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertIn</span><span class="p">(</span><span class="s1">&#39;Snart&#39;</span><span class="p">,</span> <span class="n">course</span><span class="o">.</span><span class="n">students</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_add_students</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">course</span> <span class="o">=</span> <span class="n">CourseManage</span><span class="p">(</span><span class="s2">&quot;Python&quot;</span><span class="p">)</span>
        <span class="n">name_list</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;oliver queen&#39;</span><span class="p">,</span> <span class="s1">&#39;barry allen&#39;</span><span class="p">,</span> <span class="s1">&#39;kara&#39;</span><span class="p">,</span> <span class="s1">&#39;sara lance&#39;</span><span class="p">]</span>
        <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">name_list</span><span class="p">:</span>
            <span class="n">course</span><span class="o">.</span><span class="n">add_student</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">())</span>
        <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">name_list</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">assertIn</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">(),</span> <span class="n">course</span><span class="o">.</span><span class="n">students</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div>

<h4 id="setup-fang-fa"><a class="toclink" href="#setup-fang-fa">setUp() 方法</a></h4>
<p>在上面的例子里，每个测试方法中都创建了一个实例。但是还有一种需求是，我希望只创建一个实例，然后要在多个方法里对这个实例进行操作来反复验证。在unittest.TestCase类包含方法setUp()，就可以只实例化一次，并可以在每个测试方法中使用。如果在TestCase类中包含了方法setUp()，Python会先运行它，再运行各个以test_打头的方法。<br>
简单点说，setUp()方法就是在父类里预留的一个钩子，会在其他测试方法运行前先运行：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># UnitTest/tests/course_test.py</span>
<span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">os</span>
    <span class="kn">import</span> <span class="nn">sys</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>

<span class="kn">from</span> <span class="nn">unit_test.course</span> <span class="kn">import</span> <span class="n">CourseManage</span>

<span class="k">class</span> <span class="nc">TestCourseManage</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">course</span> <span class="o">=</span> <span class="n">CourseManage</span><span class="p">(</span><span class="s2">&quot;Python&quot;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">name_list</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;oliver queen&#39;</span><span class="p">,</span> <span class="s1">&#39;barry allen&#39;</span><span class="p">,</span> <span class="s1">&#39;kara&#39;</span><span class="p">,</span> <span class="s1">&#39;sara lance&#39;</span><span class="p">]</span>

    <span class="k">def</span> <span class="nf">test_add_student</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;snart&#39;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">course</span><span class="o">.</span><span class="n">add_student</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">())</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertIn</span><span class="p">(</span><span class="s1">&#39;Snart&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">course</span><span class="o">.</span><span class="n">students</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_add_students</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">name_list</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">course</span><span class="o">.</span><span class="n">add_student</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">())</span>
        <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">name_list</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">assertIn</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">title</span><span class="p">(),</span> <span class="bp">self</span><span class="o">.</span><span class="n">course</span><span class="o">.</span><span class="n">students</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div>

<p>测试自己编写的类时，使用setUp()方法会让测试方法编写起来更容易，下面是建议的做法：
在setUp()方法中创建一系列实例并设置它们的属性，再在测试方法中直接使用这些实例。相比于在每个测试方法中都创建实例并设置其属性，这要容易得多。  </p>
<h3 id="zong-jie"><a class="toclink" href="#zong-jie">总结</a></h3>
<blockquote>
<p>如果你在项目中包含了初步测试，其他程序员将更敬佩你，他们将能够更得心应手地尝试使用你编写的代码，也更愿意与你合作开发项目。如果你要跟其他程序员开发的项目共享代码，就必须证明你编写的代码通过了既有测试，通常还需要为你添加的新行为编写测试。<br>
请通过多开展测试来熟悉代码测试过程。对于自己编写的函数和类，请编写针对其重要行为的测试，但在项目早期，不要试图去编写全覆盖的测试用例，除非有充分的理由这样做。  </p>
</blockquote>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>